const axios = require("axios").default.create({
  headers: {
    "User-Agent": "okhttp/3.6.0"
  }
});
const qs = require("querystring");
const rannum = require("random-number");
const util = require("util");
const path = require("path");
const fs = require("fs");
const js_base64 = require("js-base64");
js_base64.Base64.extendString();
const uuid = require("uuid");
const url = require("url");
const FormData = require("form-data");
const buffer = require("buffer");
const http = require("http");
const filesize = require("filesize");
const runParallel = require("run-parallel-limit");
const Toolbox = require("../Toolbox");

const CommonAxerrHandlerGen = (resolve) => (axerr) => {
  if (axerr.response) {
    resolve({
      ok: false,
      msg: `HTTP ${axerr.response.status} ${axerr.response.statusText} : ${axerr.response.data ?
        (axerr.response.data['message'] ? axerr.response.data['message'] : util.inspect(axerr.response.data)) :
        "!NO HTTP RESPONSE DATA"}`
    })
  } else {
    resolve({
      ok: false,
      msg: axerr.message ? axerr.message : util.inspect(axerr)
    });
  }
};




class PiankeOwhat {
  constructor() {
    this.token = "";
    this.userid = "";
    this.client = uuid();
    /**@type {(full_path:string,parts_count:number,part_index:number)=>string} */
    this.default_part_name_func = (full_path, parts_count, part_index) => {
      parts_count = parseInt(parts_count);
      if (parts_count == 1) {
        return path.basename(full_path);
      } else {
        let max_length = String(parts_count).length;
        let str_index = String(part_index);
        while (str_index.length < max_length) {
          str_index = `0${str_index}`
        }
        return `${path.basename(full_path)}.sf-part${str_index}`
      }
    }
  }

  /**
   *
   *
   * @returns {Promise<{ok:Boolean,msg:String}>}
   * @memberof PiankeOwhat
   */
  verify() {
    return new Promise(resolve => {
      axios.post(`http://appo4.owhat.cn/api`, "", {
        params: {
          v: "1.0",
          cmd_s: "user.account",
          requesttimestamp: Date.now(),
          cmd_m: "my",
          token: this.token,
          userid: this.userid,
          client: `{"deviceid":"${this.client}","platform":"android","version":"5.0.5","channel":"xiaomi_market"}`
        }
      }).then(axresp => {
        if (axresp.data && axresp.data.result == "success") {
          resolve({
            ok: true,
            msg: "ok"
          })
        } else {
          throw new Error(util.inspect(axresp.data));
        }
      }).catch(axerr => {
        CommonAxerrHandlerGen(resolve)(axerr);
      })
    })
  }

  /**
   * @returns {Promise<{ok:Boolean,msg:String,data:{token:String,key:String} }>}
   */
  __getUploadToken() {
    return new Promise(resolve => {
      axios.post(`http://appo4.owhat.cn/api`, "data=%7B%22imagetype%22%3A%22master.upload.image%22%7D", {
        params: {
          v: "1.0",
          cmd_s: "common",
          requesttimestamp: Date.now(),
          cmd_m: "qiniuuploadtoken",
          token: this.token || "ac6124b1b50f21d097f2d9f87ed0fa2c", //非常必要的 需要登录获得
          userid: this.userid || "6142613", //非常必要的 需要登录获得
          client: `{"deviceid":"${this.client}","platform":"android","version":"5.0.5","channel":"xiaomi_market"}`
        },
        headers: {
          "User-Agent": "okhttp/3.6.0"
        }
      }).then(axresp => {
        if (axresp.data && axresp.data.result == "success" && axresp.data.data && axresp.data.data.uploadtoken && axresp.data.data.directory) {
          resolve({
            ok: true,
            msg: "ok",
            data: {
              token: axresp.data.data.uploadtoken,
              key: `${axresp.data.data.directory}${uuid()}`
            }
          })
        } else {
          throw new Error("不符合期望" + util.inspect(axresp.data))
        }
      }).catch(axerr => {
        CommonAxerrHandlerGen(resolve)(axerr);
      })
    })
  }



  /**
   * 
   * @param {import("fs").ReadStream|String} stream 
   * @param {Strings} define_filename 
   * @returns {Promise<{ok:Boolean,msg:String,url:String}>}
   */
  uploadStream(stream, define_filename = "test.bin") {
    const upload_url = "http://upload.qiniu.com/";
    const parsed = url.parse(upload_url);
    return new Promise(async resolve => {
      let ext = path.extname(define_filename);
      ext = ext.replace(".", "")
      let o_getToken = await this.__getUploadToken(ext);
      if (!o_getToken.ok) {
        return resolve({
          ok: false,
          msg: `error when getUploadToken():${o_getToken.msg}`,
          url: ''
        })
      }
      let token = o_getToken.data.token;
      let remote_filename = o_getToken.data.key;
      let form = new FormData;
      form.append("file", stream, {
        filename: `${Math.random()}.${ext}`
      });
      form.append("key", remote_filename);
      form.append("token", token);
      let headers = {
        "User-Agent": `QiniuAndroid/7.3.0 (6.0.1; Xiaomi-Redmi Note 4X; ${Date.now()}015)`,
        // "Referer": "http://pianke.me/editor/",
        // 'X-Requested-With': 'XMLHttpRequest',
      };
      let request = http.request({
        method: "POST",
        host: parsed.host,
        path: parsed.path,
        headers: {
          ...form.getHeaders(),
          ...headers
        }
      });
      form.on("error", err => {
        resolve({
          ok: false,
          msg: `form.on(error):${err.message}`,
          url: ""
        })
      });
      form.pipe(request);
      let response_flag = false;
      form.on("end", () => {
        setTimeout(() => {
          if (!response_flag) {
            resolve({
              ok: false,
              msg: `[LATE RESPONSE] NO HTTP RESPONSE after form.end 120 seconds`
            })
          }
        }, 60 * 2 * 1000);
      })
      request.on("response", (resp) => {
        response_flag = true;
        let buf = new buffer.Buffer("");
        resp.on("data", chunk => {
          buf += chunk;
        })
        resp.on("end", $ => {
          try {
            let obj = JSON.parse(buf);
            if (!obj['key']) {
              throw new Error("json has no 'key' attributes" + util.inspect(buf))
            }
            let file_url = `https://qimage.owhat.cn/${obj['key']}`;
            resolve({
              ok: true,
              msg: "ok",
              url: file_url
            })
          } catch (e) {
            resolve({
              ok: false,
              url: "",
              msg: `error when try extract url :${e.message ? e.message : util.inspect(e)}`
            })
          }
        })
      });
      request.on("error", err => {
        resolve({
          ok: false,
          msg: `request.on(error):${err.message}`,
          url: ""
        })
      });
    })
  }

  /**
   *
   * @returns {Promise<{ok:Boolean,msg:String,files:Array<{name:String,url:String}>  }>}
   * @param {String} file_full_path
   * @param {number} [size_limit=500 * 1024 * 1024]
   * @memberof PiankeOwhat
   */
  uploadFileByMkblk(file_full_path, size_limit = 500 * 1024 * 1024) {
    return new Promise(async resolve => {
      let o_stats = await Toolbox.getStats(file_full_path);
      if (!(o_stats.ok && o_stats.stats.isFile())) {
        return resolve({
          ok: false,
          msg: o_stats.ok ? `${file_full_path} is not a file` : o_stats.msg
        })
      }
      let o_getToken = await this.__getUploadToken();
      if (!o_getToken.ok) {
        return resolve({
          ok: false,
          msg: `failed to get token : ${o_getToken.msg}`
        })
      }
      const S4MB = 4 * 1024 * 1024;
      const CHUNKS_COUNT = Math.ceil(o_stats.stats.size / S4MB);
      const basename_of_file = `${rannum()}.${path.extname(file_full_path)}`;

      /**
       * @returns {Promise<{ok:Boolean,msg:String,data:Array<{ctx:String,chunk_size:Number}> }>}
       */
      let make_blocks = () => new Promise(async mkblk_resolve => {
        /**@type {String[]} */
        const ctxs = [];
        /**@type {Number[]} */
        const chunk_sizes = []
        /**@type {Function[]} */
        let tasks = [];

        for (let i = 0; i < CHUNKS_COUNT; i++) {
          let start = i * S4MB;
          let end = start + S4MB - 1;
          if (end >= o_stats.stats.size) {
            end = o_stats.stats.size - 1;
          }
          let chunk_size = end - start + 1;
          tasks.push(async cb => {
            let headers = {
              'Authorization': `UpToken ${o_getToken.data.token}`,
              "Content-Type": "application/octet-stream",
              "Content-Length": chunk_size,
            };
            let upload_url = `http://upload.qiniup.com/mkblk/${chunk_size}?name=${encodeURI(basename_of_file)}&chunk=${i}&chunks=${CHUNKS_COUNT}`;
            let o_upChunk = await Toolbox.WrapHttpPostStream(fs.createReadStream(
              file_full_path, {
                start: start,
                end: end
              }
            ), upload_url, headers);
            // console.log(o_upChunk);
            if (o_upChunk.ok && o_upChunk.json_data && o_upChunk.json_data['ctx']) {
              cb(null, {
                index: i,
                ctx: o_upChunk.json_data['ctx'],
                chunk_size: chunk_size
              });
            } else {
              cb({
                ok: false,
                msg: `error uploading chunk ${i}:${o_upChunk.ok ? util.inspect(o_upChunk) : o_upChunk.msg}`
              })
            }
          });
        }
        runParallel(tasks, 15, (err, results) => {
          if (err) {
            return CommonAxerrHandlerGen(mkblk_resolve)(err['msg']);
          }
          results.forEach(r => {
            ctxs[r['index']] = r['ctx'];
            chunk_sizes[r['index']] = r['chunk_size'];
          });
          mkblk_resolve({
            ok: true,
            msg: "ok",
            data: ctxs.map((ctx, i) => {
              return {
                ctx: ctx,
                chunk_size: chunk_sizes[i]
              }
            })
          });
        })

      });

      let o_makeBlocks = await make_blocks();
      if (!(o_makeBlocks.data && o_makeBlocks.data.length)) {
        return resolve({
          ok: false,
          msg: `o_makeBlocks.data is ${util.inspect(o_makeBlocks.data)}`
        })
      }
      // debugger
      /**
       * @returns {Promise<{ok:Boolean,msg:String,data:Array<{mfw_fileid:String,file_name:string}>}>}
       */
      let make_file = () => new Promise(async mkfile_resolve => {
        // debugger
        /**@type {Array<Array<{ctx:String,chunk_size:number}>>} */
        let ctx_and_sizees = [];
        let max_chunk_count = Math.ceil(size_limit / S4MB);
        let i = 0;
        while (true) {
          let end_index = i + max_chunk_count - 1;
          if (end_index > o_makeBlocks.data.length - 1) {
            end_index = o_makeBlocks.data.length - 1;
          }
          ctx_and_sizees.push(o_makeBlocks.data.slice(i, end_index + 1));
          i = end_index + 1;
          if (i > o_makeBlocks.data.length - 1) {
            break;
          }
        }
        /**@type {Array<{mfw_fileid:String,file_name:string}> */
        let results = [];
        for (let part_index = 0; part_index < ctx_and_sizees.length; part_index++) {
          await new Promise(async (simple_resolve, simple_reject) => {
            let b64name = `${o_getToken.data.key}${size_limit >= o_stats.stats.size ? "" : `.sf-part${part_index + 1}`}`.toBase64(false);
            let payload = ctx_and_sizees[part_index].map(e => e.ctx).join(",");
            let part_size = ctx_and_sizees[part_index].map(e => e.chunk_size).reduce((a, b) => a + b);
            axios.post(`http://upload.qiniup.com/mkfile/${part_size}/key/${b64name}`, payload, {
              headers: {
                "Content-Length": payload.length,
                "Authorization": `UpToken ${o_getToken.data.token}`,
                "Content-Type": "text/plain;charset=UTF-8"
              },
              validateStatus: s => s == 200
            }).then(axresp => {
              if (axresp.data && axresp.data.key) {
                results.push({
                  mfw_fileid: axresp.data.key,
                  file_name: ($ => {
                    let filename = path.basename(file_full_path);
                    if (ctx_and_sizees.length > 1) {
                      let MAX_PART = ctx_and_sizees.length + "";
                      let part_str = part_index + 1 + "";
                      while (part_str.length < MAX_PART.length) {
                        part_str = `0${part_str}`
                      }
                      filename = `${filename}.sf-part${part_str}`
                    }
                    return filename;
                  })()
                });
                simple_resolve("ok")
              } else {
                throw new Error(`No sanity axresp :${util.inspect(axresp.data)}`)
              }
            }).catch(axerr => {
              if (axerr.response) {
                simple_reject(`HTTP ${axerr.response.status} ${axerr.response.statusText} : ${axerr.response.data ?
                  (axerr.response.data['message'] ? axerr.response.data['message'] : util.inspect(axerr.response.data)) :
                  "!NO HTTP RESPONSE DATA"}`)
              } else {
                simple_reject(axerr.message ? axerr.message : util.inspect(axerr))
              }
            });
          }).catch(err => {
            mkfile_resolve({
              ok: false,
              msg: `error when mkfilr part${part_index + 1}:${err}`
            })
          });
        }
        mkfile_resolve({
          ok: true,
          msg: "ok",
          data: results
        })
      });

      let o_mkfile = await make_file();
      if (!o_mkfile.ok) {
        return resolve({
          ok: false,
          msg: `error when mkfile:${o_mkfile.msg}`
        });
      }
      resolve({
        ok: true,
        msg: "ok",
        files: o_mkfile.data.map(e => {
          return {
            name: e.file_name,
            url: `https://qimage.owhat.cn/${e.mfw_fileid}`
          }
        })
      })
    })
  }


  /**
   *  @returns {Promise<{ok:Boolean,url:String,msg:String,file_size:String,speed:String}>}
   * @param {String} full_path 
   */
  getUploadSpeed(full_path) {
    return new Promise(async resolve => {
      let o_stats = await Toolbox.getStats(full_path);
      if ((!o_stats.ok) || (!o_stats.stats.isFile())) {
        return resolve({
          ok: false,
          msg: o_stats.msg ? o_stats.msg : `${full_path} is NOT File`
        })
      }
      let start_time = Date.now();
      console.log("start uploading...", path.basename(full_path));
      let o_up = await this.uploadStream(fs.createReadStream(full_path));
      let end_time = Date.now();
      if (!o_up) {
        return resolve({
          ok: false,
          msg: o_up.msg,
        })
      } else {
        let bytes_per_second = o_stats.stats.size / ((end_time - start_time) / 1000)
        resolve({
          ok: true,
          msg: "ok",
          url: o_up.url,
          file_size: filesize(o_stats.stats.size),
          speed: filesize(bytes_per_second) + "/s"
        })
      }

    })
  }

  /**
   *
   * @returns {Promise<{ok:Boolean,msg:String,files:Array<{name:String,url:String}>}>}
   * @param {String} full_file_path
   * @param {(full_path:string,parts_count:number,part_index:number)=>string} part_name_func 
   * @param {number} [size_limit=1000*1024*1024]
   * @memberof Pianke7Niu
   */
  uploadFileSimple(full_file_path, size_limit = 1000 * 1024 * 1024, part_name_func = this.default_part_name_func) {
    return new Promise(async resolve => {
      let o_stats = await Toolbox.getStats(full_file_path);
      if (!o_stats.ok) {
        return resolve({
          ok: false,
          msg: `fail to get stats of ${file_full_path} : ${o_stats.msg}`
        })
      }
      if (!o_stats.stats.isFile()) {
        return resolve({
          ok: false,
          msg: ` ${file_full_path} : it is not a file`
        })
      }
      let CHUNK_MAX_SIZE = size_limit;
      let CHUNKS_COUNT = Math.ceil(o_stats.stats.size / CHUNK_MAX_SIZE);
      /**@type {Array<{name:String,url:String}>} */
      let files = [];
      for (let i = 0; i < CHUNKS_COUNT; i++) {
        let start = i * CHUNK_MAX_SIZE;
        let end = start + CHUNK_MAX_SIZE - 1;
        if (end >= o_stats.stats.size) {
          end = o_stats.stats.size - 1;
        }
        let o_upChunk = await this.uploadStream(
          fs.createReadStream(full_file_path, {
            start: start,
            end: end
          }),
          part_name_func(full_file_path, CHUNKS_COUNT, i)
        )
        if (!o_upChunk.ok) {
          return resolve({
            ok: false,
            msg: `error when upload part ${i + 1}:${o_upChunk.msg}`
          })
        }
        files.push({
          name: part_name_func(full_file_path, CHUNKS_COUNT, i + 1),
          url: o_upChunk.url
        })
      }
      resolve({
        ok: true,
        msg: "ok",
        files: files
      })
    })
  }

  /**
   *
   * @returns {Promise<{ok:Boolean,errors:string[],files:Array<{path:string,chunks:Array<{url:string,name:string}>  }>   }>}
   * @param {String} full_path_of_dir
   * @param {number} [size_limit=1000 * 1024 * 1024]
   * @param {String} base_dir
   * @param {(full_path:string,parts_count:number,part_index:number)=>string} [part_name_func=this.default_part_name_func]
   * @memberof Pianke7Niu
   */
  safelyUploadDirSimple(full_path_of_dir, size_limit = 1000 * 1024 * 1024, base_dir = full_path_of_dir, part_name_func = this.default_part_name_func) {
    return new Promise(async resolve => {
      let o_stats = await Toolbox.getStats(full_path_of_dir);
      if (!o_stats.ok) {
        return resolve({
          ok: false,
          errors: [`fail to get stats of ${full_path_of_dir} : ${o_stats.msg}`]
        })
      }
      if (!o_stats.stats.isDirectory()) {
        return resolve({
          ok: false,
          errors: [` ${full_path_of_dir} : it is not a folder`]
        })
      }
      let safeList = await Toolbox.safeListDir(full_path_of_dir);
      /**@type {string[]} */
      let errors = [];
      /**@type {Array<{path:string,chunks:Array<{url:string,name:string}> } >} */
      let files = [];
      for (let item of safeList) {
        if (item.stats.isDirectory()) {
          let o_uploadChildFolder = await this.safelyUploadDirSimple(item.full_path, size_limit, base_dir, part_name_func);
          errors = errors.concat(o_uploadChildFolder.errors);
          files = files.concat(o_uploadChildFolder.files);
        } else if (item.stats.isFile()) {
          let o_upFile = await this.uploadFileSimple(item.full_path, size_limit, part_name_func);
          if (!o_upFile.ok) {
            errors.push(`failed to upload ${path.relative(base_dir, item.full_path)}:${o_upFile.msg}`)
          } else {
            files.push({
              path: path.relative(base_dir, item.full_path),
              chunks: o_upFile.files.map(o => {
                return {
                  url: o.url,
                  name: o.name
                }
              })
            })
          }
        }
      }
      resolve({
        ok: true,
        errors: errors,
        files: files
      })

    })
  }


  /**
   *
   * @returns {Promise<{ok:Boolean,errors:string[],files:Array<{path:string,chunks:Array<{url:string,name:string}>  }>,toHTML:()=>String   }>}
   * @param {String} full_path_of_dir
   * @param {number} [size_limit=1000 * 1024 * 1024]
   * @param {String} base_dir
   * @param {(full_path:string,parts_count:number,part_index:number)=>string} [part_name_func=this.default_part_name_func]
   * @memberof Pianke7Niu
   */
  safelyUploadDirByMkblk(full_path_of_dir, size_limit = 1000 * 1024 * 1024, base_dir = full_path_of_dir, part_name_func = this.default_part_name_func) {
    return new Promise(async resolve => {
      let o_stats = await Toolbox.getStats(full_path_of_dir);
      if (!(o_stats.ok && o_stats.stats.isDirectory())) {
        return resolve({
          ok: false,
          errors: o_stats.ok ? [`${full_path_of_dir} is not a dir`] : [`get stats error ` + o_stats.msg],
          toHTML: function () {
            return this.errors.map(e => `<font color="red">${e}</font>`).join("<br>")
          }
        })
      }
      let o_list = await Toolbox.safeListDir(full_path_of_dir);
      /**@type {string[]} */
      let errors = [];
      /**@type {Array<{path:string,chunks:Array<{url:string,name:string}> } >} */
      let files = [];
      for (let item of o_list) {
        if (item.stats.isDirectory()) {
          let o_uploadChild = await this.safelyUploadDirByMkblk(
            item.full_path,
            size_limit,
            base_dir,
            part_name_func
          );
          errors = errors.concat(o_uploadChild.errors);
          files = files.concat(o_uploadChild.files);
        } else if (item.stats.isFile()) {
          if (item.stats.size <= 50 * 1024 * 1024) {
            let o_upFile = await this.uploadFileSimple(
              item.full_path,
              size_limit,
              part_name_func
            );
            if (!o_upFile.ok) {
              errors.push(`fail to upload ${path.relative(base_dir, item.full_path)}:${o_upFile.msg}`)
            } else {
              files.push({
                path: path.relative(base_dir, item.full_path),
                chunks: o_upFile.files
              });
            }

          } else {
            let o_upFile = await this.uploadFileByMkblk(
              item.full_path,
              size_limit
            );
            if (!o_upFile.ok) {
              errors.push(`fail to upload ${path.relative(base_dir, item.full_path)}:${o_upFile.msg}`)
            } else {
              files.push({
                path: path.relative(base_dir, item.full_path),
                chunks: o_upFile.files.map(e => {
                  return {
                    url: e.url,
                    name: e.name
                  }
                })
              })
            }
          }

        }
      }

      resolve({
        ok: files.length > 0 ? true : false,
        errors: errors,
        files: files,
        toHTML: function () {
          let error_html = errors.map(e => `<font color="#BF341C">${e}</font>`).join("<hr>");
          let files_html = files.map(file => {
            if (file.chunks.length == 1) {
              return `<a href="${file.chunks[0].url}?attname=${encodeURIComponent(file.chunks[0].name)}"><font color="#171797"><u>${file.path}</u></font></a>`
            } else {
              return file.chunks.map(chunk => `<a href="${chunk.url}?attname=${encodeURIComponent(chunk.name)}"><font color="#218a21"><u>${
                path.join(path.dirname(file.path), chunk.name)
                }</u></font></a>`).join("<br>")
            }
          }).join("<br>");
          return `${error_html ? `<blockquote><h4><font color="#bf341c">ERROR LOGS : </font></h4>${error_html}</blockquote>` : ""}` +
            `<blockquote><h4><font color="#5cda5c">OK LINKS : </font></h4><p><blockquote>${files_html}</blockquote></p></blockquote>`;
        }
      })
    });
  }
};

const InstancePoll = {
  "-": new PiankeOwhat
}

class OwhatWrapper {

  /**
   * @returns {Promise<{ok:Boolean,msg:String,owhat:PiankeOwhat }>}
   * @param {String} mail 
   * @param {String} password 
   */
  static __login(mail = "hujimiya@yandex.com", password = "owhathuji") {
    return new Promise(resolve => {
      let client = uuid();
      axios.post(`http://appo4.owhat.cn/api`, qs.stringify({
        data: JSON.stringify({
          account: mail,
          password: password
        })
      }), {
          params: {
            v: "1.0",
            cmd_s: "user.account",
            requesttimestamp: Date.now(),
            cmd_m: "login",
            client: `{"deviceid":"${client}","platform":"android","version":"5.0.5","channel":"xiaomi_market"}`
          },
          headers: {
            'User-Agent': 'okhttp/3.6.0'
          },
        }).then(axresp => {
          if (axresp.data && axresp.data.result === "success" && axresp.data.data && axresp.data.data.token && axresp.data.data.userid) {
            let owhat = new PiankeOwhat;
            owhat.token = axresp.data.data.token;
            owhat.userid = axresp.data.data.userid;
            owhat.client = client;
            resolve({
              ok: true,
              msg: "ok",
              owhat: owhat
            })
          } else {
            throw new Error(util.inspect(axresp.data));
          }
        }).catch(axerr => {
          CommonAxerrHandlerGen(resolve)(axerr);
        })
    })
  }

  /**
   *
   *
   * @static
   * @param {string} [mail="hujimiya@yandex.com"]
   * @param {string} [password="owhathuji"]
   * @returns {Promise<{ok:Boolean,msg:String,instance:PiankeOwhat}>}
   * @memberof OwhatWrapper
   */
  static getValidInstance(mail = "hujimiya@yandex.com", password = "owhathuji") {
    return new Promise(async resolve => {
      let key = `${mail}:${password}`;
      if (!!InstancePoll[key]) {
        /**@type {PiankeOwhat} */
        let ow = InstancePoll[key];
        let o_verify = await ow.verify();
        if (o_verify.ok) {
          resolve({
            ok: true,
            msg: "ok",
            instance: ow
          })
        } else {
          InstancePoll[key] = null;
          let o_retry = await this.getValidInstance(mail, password);
          resolve(o_retry);
        }
      } else {
        let o_login = await this.__login(mail, password);
        if (o_login.ok) {
          InstancePoll[key] = o_login.owhat;
          resolve({
            ok: true,
            msg: "ok",
            instance: o_login.owhat
          })
        } else {
          CommonAxerrHandlerGen(resolve)(`login using ${key} failed : ${o_login.msg}`);
        }
      }

    })
  }


}

module.exports = {
  OwhatWrapper: OwhatWrapper
}